﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DNA.Mvc.Exporting;
using DNA.Mvc.Installation;
using DNA.Mvc.jQuery;
using DNA.Utility;
using DNA.IO.Compress;
using System.Web.Security;

namespace DNA.Mvc.Controllers
{
    public class SolutionController : Controller
    {
        private WebSiteContext _context;
        public SolutionController(WebSiteContext context) { _context = context; }

        [SiteControlPanel(ResKey = "RES_WEBTEMPL", Order = 3, GroupResKey = "RES_CONTENT", Group = "Content", ShowInTopSiteOnly = true)]
        public ActionResult Index()
        {
            var model = GetTeamTemplates();
            return View(model);
        }

        private IOrderedQueryable<Package> GetTeamTemplates()
        {
            var solutions = _context.DataContext.Solutions.All();
            var filter = from s in solutions
                         select new
                         {
                             template = XmlSerializerUtility.DeserializeFormXmlFile<WebTemplate>(s.ResolvePhysicalPath("install.template")),
                             solution = s
                         };
            var model = filter.Where(f => !f.template.IsTopsiteOnly).Select(f => f.solution).OrderBy(s => s.Title);
            return model;
        }

        [Pagable]
        [SecurityAction("Content", "View website list", "Allows users can view the websites list.",
             PermssionSetResName = "SA_CONTENT",
            TitleResName = "SA_ViewWebsiteList",
            DescResName = "SA_ViewWebsiteListDesc"
            )]
        [SiteControlPanel(ResKey = "RES_WEBLIST", Order = 2, GroupResKey = "RES_CONTENT", Group = "Content", ShowInTopSiteOnly = true)]
        public ActionResult List(QueryParams query)
        {
            var total = 0;
            var _resultSet = _context.DataContext.Webs.Filter(w => w.Id, w => (w.Type == 2), out total, index: query.Index - 1, size: query.Size);
            return View(new ModelWrapper<Web>(_resultSet, total));
        }

        [SecurityAction("Content", "Create team site", "Allows users can create new team site.",
                     PermssionSetResName = "SA_CONTENT",
            TitleResName = "SA_CreateTeamSite",
            DescResName = "SA_CreateTeamSiteDesc"
            )]
        public ActionResult Create(string solution)
        {
            if (string.IsNullOrEmpty(solution))
                throw new ArgumentNullException("solution");

            ViewBag.Package = _context.DataContext.Solutions.Find(p => p.Name.Equals(solution, StringComparison.OrdinalIgnoreCase));
            return View();
        }

        [HttpPost]
        public ActionResult Create(Web web, string solution)
        {
            if (_context.DataContext.Webs.Count(w => w.Name.Equals(web.Name)) > 0)
            {
                ModelState.AddModelError("", "Web site name is exists.");
            }
            else
            {
                web.Type = (int)WebTypes.Team;
                var _vals = ObjectHelper.ConvertObjectToDictionary(web);
                var settings = new Dictionary<string, object>();

                foreach (var key in _vals.Keys)
                    settings.Add("Web." + key, _vals[key]);
                settings.Add("AppPath", Server.MapPath("~/"));

                var _service = new InstallationService();
                var package = _context.DataContext.Solutions.Find(p => p.Name.Equals(solution, StringComparison.OrdinalIgnoreCase));
                var filename = package.ResolvePhysicalPath("install.xml");

                _service.Install(filename, HttpContext, settings);

                var newWeb = _context.DataContext.Webs.GetByName(web.Name);
                if (newWeb != null)
                {
                    newWeb.Type = (int)WebTypes.Team;
                    _context.DataContext.SaveChanges();
                }

                var data = _service.GetTraceResult();
                return View("Log", data);
            }

            ViewBag.Package = _context.DataContext.Solutions.Find(p => p.Name.Equals(solution, StringComparison.OrdinalIgnoreCase));
            return View(web);
        }


        [Authorize]
        public ActionResult SetupMysite()
        {
            if (!_context.EnablePersonalWeb)
                return Redirect("~/");

            if (_context.DataContext.Webs.Count(w => w.Name.Equals(User.Identity.Name) && w.Type.Equals((int)WebTypes.Personal)) > 0)
                return Redirect("~/sites/" + User.Identity.Name + "/index.html");

            ViewBag.Templates = GetTeamTemplates();
            return View();
        }

        [HttpPost]
        [Authorize]
        public ActionResult SetupMysite(Web web, string solution)
        {
            if (_context.DataContext.Webs.Count(w => w.Name.Equals(web.Name)) > 0)
            {
                ModelState.AddModelError("", "You already have personal website.");
            }
            else
            {
                web.Type = (int)WebTypes.Personal;
                var _vals = ObjectHelper.ConvertObjectToDictionary(web);
                var settings = new Dictionary<string, object>();

                foreach (var key in _vals.Keys)
                    settings.Add("Web." + key, _vals[key]);
                settings.Add("AppPath", Server.MapPath("~/"));

                var _service = new InstallationService();
                var package = _context.DataContext.Solutions.Find(p => p.Name.Equals(solution, StringComparison.OrdinalIgnoreCase));
                var filename = package.ResolvePhysicalPath("install.xml");

                _service.Install(filename, HttpContext, settings);

                var newWeb = _context.DataContext.Webs.GetByName(web.Name);
                if (newWeb != null)
                {
                    newWeb.Type = (int)WebTypes.Personal;
                    _context.DataContext.SaveChanges();
                }

                var _defaultUrl = newWeb.DefaultUrl;
                if (string.IsNullOrEmpty(_defaultUrl))
                    _defaultUrl = UrlResolver.Resolve(newWeb) + "/index.html";
                return Redirect(_defaultUrl);
            }

            ViewBag.Templates = GetTeamTemplates();
            return View(web);
        }

        [Authorize]
        public FileResult Download(string name)
        {
            var packer = new Packer(HttpContext);
            var stream = new System.IO.MemoryStream();
            var pkg = _context.DataContext.Solutions.Find(t => t.Name.Equals(name, StringComparison.OrdinalIgnoreCase));

            var tmp = WebSiteContext.GetTemporaryDirectory(HttpContext);// HttpContext.Server.MapPath("~/Shared/$tmp/") + Path.GetRandomFileName().Replace(".", "");
            var _path = pkg.ResolvePhysicalPath();
            var images = new List<string>();
            if (pkg.Images.Count > 0)
            {
                foreach (var img in pkg.Images)
                    images.Add(_path.EndsWith("\\") ? (_path + img.Url) : (_path + "\\" + img.Url));
            }

            if (!Directory.Exists(tmp))
                Directory.CreateDirectory(tmp);

            var src = HttpContext.Server.MapPath("~/Shared/packages/" + name);
            FileUtility.CopyDirectory(src, tmp);
            using (var file = System.IO.File.OpenWrite(tmp + ".zip"))
            {
                var zipPackage = new DNA.IO.Compress.ZipCompress(file, tmp);
                zipPackage.AddPath(tmp);
                zipPackage.Compress();
            }

            System.IO.File.WriteAllText(tmp + "\\install.xml", "<?xml version=\"1.0\" encoding=\"utf-8\" ?><installers><installer name=\"unzip\"><config src=\"" + name + ".zip\" destination=\"~/shared/packages/" + name + "\" /></installer></installers>");
            var zip = new DNA.IO.Compress.ZipCompress(stream, tmp);

            zip.AddFile(tmp + "\\install.xml", "install.xml");
            zip.AddFile(tmp + ".zip", name + ".zip");
            zip.AddFile(tmp + "\\manifest.xml", "manifest.xml");
            foreach (var img in images)
                zip.AddFile(img, img.Replace(_path, ""));

            zip.Compress();
            stream.Position = 0;
            Directory.Delete(tmp, true);

            return File(stream, "application/x-zip-compressed", name + ".zip");
        }

        public ActionResult Export()
        {
            ViewBag.Exporters = WebSiteContext.GetServices<Exporter>().OrderBy(o => o.Order);
            return View();
        }

        [HttpPost]
        public ActionResult Export(Package package, FormCollection forms)
        {
            var selectedExportNames = new List<string>();
            foreach (var key in forms.AllKeys)
            {
                if (key.StartsWith("Exporter.", StringComparison.OrdinalIgnoreCase))
                {
                    var selected = bool.Parse(forms[key]);
                    if (selected)
                        selectedExportNames.Add(key.Split('.')[1]);
                }
            }

            if (selectedExportNames.Count == 0)
            {
                ViewData.ModelState.AddModelError("", "Please select a exporter at less.");
                return View(package);
            }

            var exporters = WebSiteContext.GetServices<Exporter>();

            if (exporters.Count() == 0)
            {
                ViewData.ModelState.AddModelError("", "No available exporter found. Could not export any content of using!");
                return View(package);
            }

            var selectedExporters = exporters.Where(e => selectedExportNames.Contains(e.Name) || !e.Visible).OrderBy(e => e.Order).ToList();

            if (ModelState.IsValid)
            {
                var tmpPath = WebSiteContext.GetTemporaryDirectory(HttpContext);

                if (!Directory.Exists(tmpPath))
                    Directory.CreateDirectory(tmpPath);

                if (Request.Files.Count > 0)
                {
                    for (int i = 0; i < Request.Files.Count; i++)
                    {
                        var file = Request.Files[i];
                        if ((file.ContentLength > 0) && (!string.IsNullOrEmpty(file.FileName)))
                        {
                            var srvFileName = tmpPath + Path.GetFileName(file.FileName);
                            file.SaveAs(srvFileName);
                            package.Images.Add(new ImageRes() { Url = srvFileName, IsDefault = (i == 0 ? true : false) });
                        }
                    }
                }

                var container = new ExporterContainer(selectedExporters);

                package.Authors = new List<Contact>();

                if (string.IsNullOrEmpty(package.Title))
                    package.Title = package.Name;

                var user = Membership.GetUser(User.Identity.Name);

                var userUri = "";
                if (HttpContext.Profile["WebSite"] != null && !string.IsNullOrEmpty(HttpContext.Profile["WebSite"].ToString()))
                    userUri = HttpContext.Profile["WebSite"].ToString();

                var author = new Contact()
                {
                    Name = user.GetDisplayName(),
                    Email = user.Email,
                    WebSite = userUri
                };

                package.Authors.Add(author);

                var _params = new
                {
                    Package = package,
                    Web = _context.Web,
                    ThemePath = "~/content/themes/" + _context.Web.Theme + "/",
                    Language = System.Threading.Thread.CurrentThread.CurrentCulture.Name
                };

                var compressPath = WebSiteContext.GetTemporaryDirectory(HttpContext);
                var compressFileName = compressPath + ".zip";

                if (!compressPath.EndsWith("\\"))
                    compressPath += "\\";

                if (!Directory.Exists(compressPath))
                    Directory.CreateDirectory(compressPath);

                container.Process(compressPath, _params, HttpContext);

                using (var compressStream = System.IO.File.OpenWrite(compressFileName))
                {
                    var zip = new ZipCompress(compressStream, compressPath);
                    zip.AddPath(compressPath);
                    zip.Compress();
                    zip.Dispose();
                }
                var logData = new List<TraceData>();
                ViewBag.FileName = "";

                try
                {
                    Directory.Delete(tmpPath, true);
                    Directory.Delete(compressPath, true);
                    var data = container.GetTraceResult();
                    logData.AddRange(data);
                }
                catch (Exception e)
                {
                    if (System.IO.File.Exists(compressFileName))
                        System.IO.File.Delete(compressFileName);

                    logData.Add(new TraceData("Export fail! " + e.Message) { ErrorInfo = e, Category = "Error", IsWarn = true });
                    return View("SaveLog", logData);
                }

                if (logData.FirstOrDefault(t => t.IsError) != null)
                    logData.Add(new TraceData("Export fail some error occurs!") { Category = "Error", IsWarn = true });
                else
                    logData.Add(new TraceData("Export successful!"));

                var wsPath = Server.MapPath(_context.Web.IsRoot ? "~/shared/public/packages/" : "~/shared/personal/" + _context.Web.Name + "/packages/");
                var wsFileName = TextUtility.Slug(package.Name) + ".zip";

                if (!Directory.Exists(wsPath))
                    Directory.CreateDirectory(wsPath);

                var wpFilename = wsPath + wsFileName;
                if (System.IO.File.Exists(wpFilename))
                    System.IO.File.Delete(wpFilename);

                System.IO.File.Move(compressFileName, wpFilename);

                ViewBag.FileName = _context.Web.WebFolder + "packages/" + wsFileName;// compressFileName;

                return View("SaveLog", logData);
            }

            return View(package);
        }
    }
}
